package com.tramp.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.tramp.frame.server.exception.GenericException;

public final class DateUtil {

	protected static final Logger LOGGER = LoggerFactory.getLogger(DateUtil.class);
	public static final long ONE_SECOND_MILLS = 1000L;
	public static final long ONE_MINUTE_MILLS = 60 * ONE_SECOND_MILLS;
	public static final long ONE_HOUR_MILLS = 60 * ONE_MINUTE_MILLS;
	public static final long ONE_DAY_MILLS = 24 * ONE_HOUR_MILLS;
	public static final String YYYYMMDDHH24MMSS = "yyyyMMddHHmmss";
	public static final String YYYY_MM_DD = "yyyy-MM-dd";
	public static final String YYYY_MM_DD_HH_24MM = "yyyy-MM-dd HH:mm";
	public static final String YYYY_MM_DD_HH_24MM_SS = "yyyy-MM-dd HH:mm:ss";
	public static final int SEVEN = 7;

	private DateUtil() {
	}

	/**
	 * 日期属于第几周
	 * @param date 日期
	 * @return
	 */
	public static int getWeekOfYear(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setFirstDayOfWeek(Calendar.MONDAY); // 设置每周的第一天为星期一
		cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);// 每周从周一开始
		cal.setMinimalDaysInFirstWeek(4);
		cal.setTime(date);
		return cal.get(Calendar.WEEK_OF_YEAR);
	}

	/**
	 * 日期属于周报的年份
	 * @param date 日期
	 * @return
	 */
	public static int getYearOfWeekly(Date date) {
		//当前年份
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int year = cal.get(Calendar.YEAR);
		int week = getWeekOfYear(date);
		//上周
		Date preWeekDate = DateUtils.addWeeks(date, -1);
		cal.setTime(preWeekDate);
		int preWeekYear = cal.get(Calendar.YEAR);
		//下周
		Date nextWeekDate = DateUtils.addWeeks(date, 1);
		cal.setTime(nextWeekDate);
		int nextWeekYear = cal.get(Calendar.YEAR);
		int nextWeek = getWeekOfYear(nextWeekDate);

		//处理跨年情况
		if (preWeekYear == year && week == 1) {
			return year + 1;
		}
		else if (year == nextWeekYear && nextWeek == 1) {
			return year - 1;
		}
		else {
			return year;
		}
	}

	/**
	 * 计算某年某周的开始日期
	 *
	 * @param year 年份
	 * @param week 周数
	 * @return
	 */
	public static long getYearWeekFirstDay(int year, int week) {
		Calendar cal = Calendar.getInstance();
		cal.setFirstDayOfWeek(Calendar.MONDAY); // 设置每周的第一天为星期一
		cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);// 每周从周一开始
		// 上面两句代码配合，才能实现，每年度的第一个周，是包含第一个星期一的那个周。
		cal.setMinimalDaysInFirstWeek(4);
		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.WEEK_OF_YEAR, week);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTimeInMillis();
	}

	/**
	 * 计算某年某周的结束日期
	 *
	 * @param year 年份
	 * @param week 周数
	 * @return
	 */
	public static long getYearWeekEndDay(int year, int week) {
		Calendar cal = Calendar.getInstance();
		cal.setFirstDayOfWeek(Calendar.MONDAY); // 设置每周的第一天为星期一
		cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);// 每周从周一开始
		// 上面两句代码配合，才能实现，每年度的第一个周，是包含第一个星期一的那个周。
		cal.setMinimalDaysInFirstWeek(4);
		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.WEEK_OF_YEAR, week);
		cal.set(Calendar.HOUR_OF_DAY, 23);
		cal.set(Calendar.MINUTE, 59);
		cal.set(Calendar.SECOND, 59);
		cal.set(Calendar.MILLISECOND, 999);
		return cal.getTimeInMillis();
	}

	/**
	 *
	 * 获取本周几的日期，返回的默认日期格式为2017-09-08
	 * @param weekDay 周几，如要本周一的日期，则传1，周二就传2
	 * @param format 返回的日期格式
	 * @return
	 */
	public static String getDateWeek(int weekDay, String format) {
		if (weekDay < 1 || weekDay > SEVEN) {
			throw new RuntimeException("只能传1到7的数字");
		}
		Calendar cal = Calendar.getInstance();
		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
		cal.set(Calendar.DAY_OF_WEEK, weekDay + 1);

		Date date = cal.getTime();
		if (weekDay == SEVEN) { //周日比较特殊，我们的周日是老美下周的第一天
			cal.setTime(date);
			cal.add(Calendar.DAY_OF_WEEK, SEVEN);
			date = cal.getTime();
		}
		return longToString(date.getTime(), format);
	}

	/**
	 * 获取本周几的日期，返回的默认日期格式为2017-09-08
	 * @param weekDay 周几，如要本周一的日期，则传1，周二就传2
	 * @return
	 */
	public static String getDateWeek(int weekDay) {
		return getDateWeek(weekDay, YYYY_MM_DD);
	}

	/**
	 *
	 * @param time 时间字符串
	 * @return
	 */
	public static final Date string2Date(String time) {
		return new Date(stringToLong(time));
	}

	/**
	 * 获取当前时间，格式：yyyyMMddHHmmss
	 * @return
	 */
	public static final String getTimestamp() {
		return getTimestamp(YYYYMMDDHH24MMSS);
	}

	/**
	 * 获取指定格式的当前时间
	 * @param format
	 * @return
	 */
	public static final String getTimestamp(String format) {
		return new SimpleDateFormat(format).format(new Date());
	}

	/**
	 * 半小时后的时间戳
	 * @return
	 */
	public static long nextHalfHours(Date datetime) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(datetime);
		calendar.add(Calendar.MINUTE, 30);
		return calendar.getTimeInMillis();
	}

	/**
	 * N分钟后的时间戳
	 * @return
	 */
	public static long nextMinutes(Date datetime, int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(datetime);
		calendar.add(Calendar.MINUTE, n);
		return calendar.getTimeInMillis();
	}

	/**
	 * N分钟后的时间戳
	 * @return
	 */
	public static long nextMinutes(int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MINUTE, n);
		return calendar.getTimeInMillis();
	}

	public static long nextSecond(int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.SECOND, n);
		return calendar.getTimeInMillis();
	}

	/**
	 * 上个月
	 * @param n
	 * @return
	 */
	public static Date priMonth(int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MONTH, (0 - n));
		return new Date(calendar.getTimeInMillis());
	}

	/**
	 * 某个日期的前一个星期
	 * @param date
	 * @return
	 */
	public static Date preWeek(Date date) {
		return DateUtils.addWeeks(date, -1);
	}

	/**
	 * 某个日期的前N个星期
	 * @param date
	 * @return
	 */
	public static Date preWeeks(Date date, int weeks) {
		return DateUtils.addWeeks(date, -weeks);
	}

	/**
	 * 前一天的开始时间戳
	 * @return
	 */
	public static long yesterdayBeginTime() {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.DATE, -1);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTimeInMillis();
	}

	/**
	 * 当天开始时间戳
	 * @return
	 */
	public static long todayBeginTime() {
		Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTimeInMillis();
	}

	/**
	 * n小时后的时间戳，若n是负数，则返回n小时前的时间戳
	 * @param n
	 * @return
	 */
	public static long lastHours(Date datetime, int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(datetime);
		calendar.add(Calendar.HOUR_OF_DAY, (-1) * n);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTimeInMillis();
	}

	/**
	 * n天后的时间戳，若n是负数，则返回n天前的时间戳
	 * @param n
	 * @return
	 */
	public static long nextDays(int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.DATE, n);
		return calendar.getTimeInMillis();
	}

	/**
	 * 某个时间点n天后的时间戳，若n是负数，则返回某个时间点n天前的时间戳
	 * @param time 某个时间戳，long型
	 * @param n
	 * @return
	 */
	public static long nextDays(long time, int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date(time));
		calendar.add(Calendar.DATE, n);
		return calendar.getTimeInMillis();
	}

	/**
	 * 当前时间半小时前的时间戳
	 * @return
	 */
	public static long lastHalfHours() {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.HOUR_OF_DAY, -1);
		calendar.set(Calendar.MINUTE, 30);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTimeInMillis();
	}

	/**
	 * 当前时间n个月后的时间戳，若n是负数，则返回n个月前的时间戳
	 * @param n
	 * @return
	 */
	public static Date nextMonth(int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MONTH, n);
		return new Date(calendar.getTimeInMillis());
	}

	/**
	 * 指定时间n个月后的时间戳，若n是负数，则返回n个月前的时间戳
	 * @param n
	 * @return
	 */
	public static Date nextMonth(long time, int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date(time));
		calendar.add(Calendar.MONTH, n);
		return new Date(calendar.getTimeInMillis());
	}

	/**
	 * 获取当月最后时刻的时间戳
	 * @return
	 */
	public static Date monthEnd() {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MONTH, 1);
		calendar.set(Calendar.DAY_OF_MONTH, 0);
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
		calendar.set(Calendar.SECOND, 59);
		calendar.set(Calendar.MILLISECOND, 999);
		return new Date(calendar.getTimeInMillis());
	}

	/**
	 * 获取某个时间点当月最后时刻的时间戳
	 * @param time
	 * @return
	 */
	public static Date getMonthEndFromMills(long time) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(time);
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
		calendar.set(Calendar.SECOND, 59);
		calendar.set(Calendar.MILLISECOND, 999);
		return calendar.getTime();
	}

	/**
	 * 获取某个时间点当月最后时刻
	 * @param date
	 * @return
	 */
	public static Date getDayEndFromDate(Date date) {
		if (null == date) {
			return date;
		}
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(date.getTime());
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
		calendar.set(Calendar.SECOND, 59);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTime();
	}

	/**
	 * 获取某个时间点下个月最后时刻的时间戳
	 * @param time
	 * @return
	 */
	public static Date getNextMonthEndFromMills(long time) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(time);
		calendar.add(Calendar.MONTH, 1);
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
		calendar.set(Calendar.SECOND, 59);
		calendar.set(Calendar.MILLISECOND, 999);
		return calendar.getTime();
	}

	public static Date getMonthStartFromMills(long time) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(time);
		calendar.setFirstDayOfWeek(Calendar.MONDAY);
		calendar.set(Calendar.DAY_OF_MONTH, 1);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTime();
	}

	/**
	 * 当前时间n年后的时间戳，若n是负数，则返回n年前的时间戳
	 * @param n
	 * @return
	 */
	public static Date nextYear(long time, int n) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date(time));
		calendar.add(Calendar.YEAR, n);
		return new Date(calendar.getTimeInMillis());
	}

	/**
	 * 计算两个时间的相差
	 * @param startTime 开始时间戳，long型
	 * @param endTime 结束时间戳，long型
	 * @param type 比较的单位，Calendar.DATE：天，Calendar.HOUR：小时，Calendar.SECOND：分钟，Calendar.SECOND：秒
	 * @return
	 */
	public static long dateDiff(long startTime, long endTime, int type) {
		long result = 0L;
		long diff;
		try {
			// 获得两个时间的毫秒时间差异
			diff = endTime - startTime;
			switch (type) {
			case Calendar.DATE:
				result = diff / ONE_DAY_MILLS; // 计算差多少天
				break;
			case Calendar.HOUR:
				result = diff % ONE_DAY_MILLS / ONE_HOUR_MILLS; // 计算差多少小时
				break;
			case Calendar.MINUTE:
				result = diff % ONE_DAY_MILLS % ONE_HOUR_MILLS / ONE_MINUTE_MILLS; // 计算差多少分钟
				break;
			case Calendar.SECOND:
				result = diff % ONE_DAY_MILLS % ONE_HOUR_MILLS % ONE_MINUTE_MILLS / ONE_SECOND_MILLS;
				// 计算差多少秒
				break;
			default:
				break;
			}
		}
		catch (Exception e) {
			LOGGER.error("error:", e);
		}
		return result;
	}

	/**
	 * 计算两个时间的相差
	 * @param startTime 开始时间，Date型
	 * @param endTime 结束时间，Date型
	 * @param type 比较的单位，Calendar.DATE：天，Calendar.HOUR：小时，Calendar.SECOND：分钟，Calendar.SECOND：秒
	 * @return
	 */
	public static long dateDiff(Date startTime, Date endTime, int type) {
		long startTimeLong = startTime.getTime();
		long endTimeLong = endTime.getTime();
		return dateDiff(startTimeLong, endTimeLong, type);
	}

	/**
	 * 计算两个时间的天数相差
	 *
	 * @param startTime 开始时间，String型
	 * @param endTime   结束时间，String型
	 * @return
	 */
	public static long dateDiff(String startTime, String endTime) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		try {
			long startTimeLong = sdf.parse(startTime).getTime();
			long endTimeLong = sdf.parse(endTime).getTime();
			return dateDiff(startTimeLong, endTimeLong, Calendar.DATE);
		}
		catch (ParseException e) {
			LOGGER.error("error:", e);
		}
		return -1L;
	}

	/**
	 * 获取月份开始时间
	 *
	 * @param next 月份偏移，0表示当月，1表示下个月
	 * @return
	 */
	public static long getMonthStart(int next) {
		return getMonthStartFromMills(nextMonth(next).getTime()).getTime();
	}

	/**
	 * 获取月份结束时间
	 *
	 * @param next 月份偏移，0表示当月，1表示下个月
	 * @return
	 */
	public static long getMonthEnd(int next) {
		return getMonthEndFromMills(nextMonth(next).getTime()).getTime();
	}

	public static long getDayStartFromMills(long mills) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(mills);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTimeInMillis();
	}

	public static long getDayEndFromMills(long mills) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(mills);
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
		calendar.set(Calendar.SECOND, 59);
		calendar.set(Calendar.MILLISECOND, 999);
		return calendar.getTimeInMillis();
	}

	public static long getTodayStart() {
		return getDayStartFromMills(System.currentTimeMillis());
	}

	public static long getTodayEnd() {
		Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
		calendar.set(Calendar.SECOND, 59);
		calendar.set(Calendar.MILLISECOND, 59);
		return calendar.getTimeInMillis();

	}

	public static String longToString(long time) {
		return longToString(time, null);

	}

	public static String longToString(long time, String format) {
		TimeZone tz = TimeZone.getTimeZone("GMT+8");
		if (format == null) {
			format = "yyyyMMdd HH:mm";
		}
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(time);

		return DateFormatUtils.format(calendar, format, tz, Locale.US);

	}

	public static long stringToLong(String time) {
		return stringToLong(time, null);

	}

	public static boolean isToday(long times) {
		return DateUtils.isSameDay(new Date(), new Date(times));
	}

	public static long stringToLong(String time, String[] format) {
		if (format == null) {
			format = new String[] { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH", YYYY_MM_DD, "yyyy-MM", "yyyyMMdd HH:mm:ss",
					"yyyyMMdd HH:mm", "yyyyMMdd HH", "yyyy/MM/dd", "yyyy/MM", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM.dd HH",
					"yyyy.MM.dd", "yyyyMMddHHmmss", "yyyyMMdd", "yyyyMM" };
		}
		Date date = null;
		try {
			date = DateUtils.parseDate(time, format);
			return date.getTime();
		}
		catch (ParseException e) {
			throw new GenericException("stringToLong fail");
		}
	}

	public static long now() {
		return System.currentTimeMillis();
	}

	/**
	 * 获取今天剩余的秒数
	 *
	 * @return
	 */
	public static Long getTodayRestSeconds() {
		long tomorromStart = getTodayEnd() + 1000;
		long now = now();
		return (tomorromStart - now) / 1000;
	}

	public static List<String> getQueryDateList(String startTime, String endTime) {
		try {
			if (StringUtils.isAnyBlank(startTime, endTime)) {
				LOGGER.error(" getQueryDateList param null, startTime:{}, endTime:{}", startTime, endTime);
				return Collections.emptyList();
			}
			if (startTime.compareTo(endTime) > 0) {
				LOGGER.error(" getQueryDateList param invalid, startTime:{}, endTime{}", startTime, endTime);
				return Collections.emptyList();
			}
			List<String> queryDateList = Lists.newArrayList();
			long startTimeLong = stringToLong(startTime);
			long days = dateDiff(startTimeLong, stringToLong(endTime), Calendar.DATE) + 1;
			for (int i = 0; i < days; i++) {
				queryDateList.add(longToString(nextDays(startTimeLong, i), YYYY_MM_DD));
			}
			return queryDateList;
		}
		catch (Exception e) {
			LOGGER.error("getQueryDateList error, startTime:{}, endTime:{}", startTime, endTime, e);
		}
		return Collections.emptyList();
	}

	/**
	 * 查询两个日期之间的月份
	 * @param minDate 开始时间
	 * @param maxDate 结束时间
	 * @return
	 */
	public static List<String> getMonthBetween(String minDate, String maxDate) {
		ArrayList<String> result = Lists.newArrayList();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");//格式化为年月

		Calendar min = Calendar.getInstance();
		Calendar max = Calendar.getInstance();

		try {
			min.setTime(sdf.parse(minDate));
			min.set(min.get(Calendar.YEAR), min.get(Calendar.MONTH), 1);

			max.setTime(sdf.parse(maxDate));
			max.set(max.get(Calendar.YEAR), max.get(Calendar.MONTH), 2);
		}
		catch (Exception e) {
			LOGGER.error("getMonthBetween error ", e);
		}

		Calendar curr = min;
		while (curr.before(max)) {
			result.add(sdf.format(curr.getTime()));
			curr.add(Calendar.MONTH, 1);
		}

		return result;
	}

	/**
	 * 取得当前日期（日期格式：yyyyMMdd）
	 *
	 * @return 日期字符串
	 */
	public static String getNowdateYYYYMMDD() {
		Date date = new Date();
		SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
		String nowdate = format.format(date);
		return nowdate;
	}
}
